
/*-------------------------------------------------------*/
/*  Input Manager                                        */
/*  [c]copyright 1995 by AlphaHelix                      */
/*                                                       */
/*                                                       */
/*-------------------------------------------------------*/

#include <i86.h>
#include <dos.h>
#include <conio.h>

#include "error.hpp"
#define AH_INPUTMANAGER
#include "input.hpp"

// Globaler Schrott

#define MAXINPUTS          4     // Number of input devices supported.

// Next struct is a REGISTER structure for all input devices available.
// A device must be registered in this structure to get active.
// I know the idea is good but it's not implemented strict enough to earn
// the title CLEAN PROGRAMMING.
struct Device {
   int      locked;              // Device has been locked.
   char     *name;               // Device's name.
   void     (*getmotion)(int &, int &, int &);
};


//-------------------------------------------------------------------
// Keyboard interface routines.
//-------------------------------------------------------------------
// Private data.
// Defined movement keys.
static int  key_left = 0x4b;
static int  key_right = 0x4d;
static int  key_up = 0x48;
static int  key_down = 0x50;
static int  key_fire = 0x39;

static void (__interrupt __far *int09)(void);    // Pointer to old keyboard interrupt.


static void __interrupt __far newint09(void)
{
   int   scan;
   int   status;

// Get scan code.
   scan = inp(0x60);
// Fetch status information.
   status = inp(0x61);
// Re-enable keyboard.
   outp(0x61, status | 0x80);
// Write back old value.
   outp(0x61, status);

// Analyze scan code.
   if (scan & 0x80) {
      scan &=0x7f;         // Clear 'key pressed' bit.
      key_released = scan; // Store last released key.
      if (key[scan]) {
         key[scan] = 0;
         keys--;
      }
   } else {
      key_pressed = scan;  // Store last pressed key.
      if (!key[scan]) {
         key[scan] = 1;
         keys++;
      }
   }

// Do PIC housekeeping.
   outp(0x20, 0x20);

// Quit if CTRLALTDEL has been pressed.
   if (key[0x1d] && key[0x38] && key[0x53]) error("James Bond was here.");
}

/*---------------------------------------------------------
 Function: keyboard_wait

 Description:
 Waits until all keyboard keys have been released.
---------------------------------------------------------*/
void keyboard_wait(void)
{
   while (keys);
}

void getkeyboard(int &x, int &y, int &fire)
{
   x = 0; y = 0; fire = 0;
   if (key[key_left]) x--;
   if (key[key_right]) x++;
   if (key[key_up]) y--;
   if (key[key_down]) y++;
   if (key[key_fire]) fire = 1;
}

void setkeys(int left, int right, int up, int down, int fire)
{
   key_left = left;
   key_right = right;
   key_up = up;
   key_down = down;
   key_fire = fire;
}

static void initkeyboard(void)
{
   int   i;

// First clear variables.
   keys = 0;
   for (i = 0; i < 0x80; i++) {
      key[i] = 0;
   }

// Please remember: Perhaps we have to lock the memory and use DPMI
// get/setvect instead. (Testing is needed under other DPMI hosts.)

//   getvect(0x09, int09_sel, int09_ofs);
//   setvect(0x09, FP_SEG(newint09), FP_OFF(newint09));

   int09 = _dos_getvect(0x09);
   _dos_setvect(0x09, newint09);

   keyboard = 1;     // Indicate "keyboard present".
}

static void shutkeyboard(void)
{
   if (keyboard) {
//    setvect(0x09, int09_sel, int09_ofs);
      _dos_setvect(0x09, int09);
      keyboard = 0;
   }
}


//-------------------------------------------------------------------
// Joystick interface routines.
//-------------------------------------------------------------------
#define JOYSTICK        0x201          // Joystick port.
#define JOYA_X          0x01
#define JOYA_Y          0x02
#define JOYA_B1         0x10
#define JOYA_B2         0x20

#define JOY_TIMEOUT     20000          // Joystick timeout value.

static   int   x0, y0, x1, y1;         // Joystick corner values.

static int measure(int &x, int &y, int &fire)
{
   int      x_count, y_count;
   int      timeout;
   int      tmp;

// Start new measure.
   outp(JOYSTICK, 0);
   x_count = y_count = 0;
   timeout = JOY_TIMEOUT;

   _disable();     // No interrupts now.
   do {
// Read JOYSTICK port. If bits 0 and 1 are 0 then we are done.
         tmp = inp(JOYSTICK);
// Check x of joypad A.
      if ((tmp & JOYA_X)) x_count++;
// Check y of port A.
      if ((tmp & JOYA_Y)) y_count++;
   } while ((tmp & (JOYA_X | JOYA_Y)) && (--timeout));
   _enable();      // Interrupts are welcome again.

// Check FIRE button.
   fire = !(tmp & JOYA_B1) || !(tmp & JOYA_B2);

   x = x_count;
   y = y_count;
   return (timeout ? 1 : 0);

}

/*---------------------------------------------------------
 Function: joypad_wait

 Description:
 Waits until the joypad fire button has been released.
---------------------------------------------------------*/
void joypad_wait(void)
{
   int   i;
   int   x, y, fire;

   for (i = 0; i < 10; i++) {
      do {
         measure(x, y, fire);
      } while (fire);
      delay(10);
   }
}


void getjoypad(int &x, int &y, int &fire)
{
   int   dx, dy;

   x = y = 0;
   measure(dx, dy, fire);
   if (dx < x0) x--;
   if (dy < y0) y--;
   if (dx > x1) x++;
   if (dy > y1) y++;

}


int calibrate_joypad(void)
{
   int   x, y, fire;
   int   yes;

   yes = measure(x, y, fire);
   x0 = x - (x / 2);
   x1 = x + (x / 2);
   y0 = y - (y / 2);
   y1 = y + (y / 2);
   return yes;
}


void initjoypad(void)
{
// Check for presence of joypad.
   joypad = calibrate_joypad();
}

void shutjoypad(void)
{
   joypad = 0;
}


//-------------------------------------------------------------------
// Mouse interface routines.
//-------------------------------------------------------------------

static int  m_sensx;          // Mouse sensitivity x direction.
static int  m_sensy;          // Mouse sensitivity y direction.
static int  m_sens;           // Mouse sensitivity level. 0=low, 2=high.

/*---------------------------------------------------------
 Function: getmouse

 Description:
 Get mouse button state and mouse position.
---------------------------------------------------------*/
void getmousexy(int &b, int &x, int &y)
{
   union REGS  regs;

   regs.w.ax = 3;          // function 3: get mouse position.
   int386(0x33, &regs, &regs);
   b = regs.w.bx;
   x = regs.w.cx;
   y = regs.w.dx;
}

// Return the net mouse displacement since the last call to this function.
void getmousemotion(int &dx, int &dy)
{
   union REGS  regs;

   regs.w.ax = 0xb;          // function b: get mouse motion.
   int386(0x33, &regs, &regs);
   dx = (short)regs.w.cx;
   dy = (short)regs.w.dx;
}



/*---------------------------------------------------------
 Function: setmousexy

 Description:
 Set mouse position
---------------------------------------------------------*/
void setmousexy(int x, int y)
{
   union REGS  regs;

   regs.w.ax = 4;
   regs.w.cx = x;
   regs.w.dx = y;
   int386(0x33, &regs, &regs);
}

void setmouselimits(int x0, int y0, int x1, int y1)
{
   union REGS  regs;

// Set horizontal limits first.
   regs.w.ax = 7;
   regs.w.cx = x0;
   regs.w.dx = x1;
   int386(0x33, &regs, &regs);

// Go for the vertical limits now.
   regs.w.ax = 8;
   regs.w.cx = y0;
   regs.w.dx = y1;
   int386(0x33, &regs, &regs);
}


/*---------------------------------------------------------
 Function: mouse_wait

 Description:
 Waits until all mouse buttons have been released.
---------------------------------------------------------*/
void mouse_wait(void)
{
   int   x, y, b;

   getmousexy(b, x, y);
   while (b) getmousexy(b, x, y);
}

void setmousesens(int sens)
{
   switch (sens) {
   case M_LOW:
      m_sensx = 2;
      m_sensy = 3;
      break;
   case M_MEDIUM:
      m_sensx = 1;
      m_sensy = 2;
      break;
   case M_HIGH:
      m_sensx = 0;
      m_sensy = 1;
   }
   m_sens = sens;
}

int getmousesens(void)
{
   return m_sens;
}

void getmouse(int &x, int &y, int &fire)
{
   int   dx, dy;

   x = y = 0;
   getmousexy(fire, dx, dy);
   getmousemotion(dx, dy);
   if (dx > m_sensx) x++;
   if (dx < -m_sensx) x--;
   if (dy > m_sensy) y++;
   if (dy < -m_sensy) y--;
}


void initmouse(void)
{
   union    REGS regs;

   regs.w.ax = 0;          // Reset mouse & get status.
   int386(0x33, &regs, &regs);
   if (regs.w.ax != 0) {
// Mouse is present.
      mouse = 1;
      setmousesens(M_MEDIUM);
   } else {
      mouse = 0;
   }

}

void shutmouse(void)
{
   union    REGS regs;

   if (mouse) {
      regs.w.ax = 0;          // Reset mouse & get status.
      int386(0x33, &regs, &regs);
      mouse = 0;
   }
}


//-------------------------------------------------------------------
// Input Manager code & data.
//-------------------------------------------------------------------

static int     ndevices;               // Number of devices available.
static Device  device[MAXINPUTS];      // Device description.

/*---------------------------------------------------------
 Function: input_wait

 Description:
 Wait until all enabled input devices have been released.
---------------------------------------------------------*/
void input_wait(void)
{
   if (keyboard) keyboard_wait();
   if (mouse) mouse_wait();
   if (joypad) joypad_wait();
}

/*---------------------------------------------------------
 Function: input_fire

 Description:
 Waits until fire button on any enabled device has been
 pressed.
---------------------------------------------------------*/
void input_fire(void)
{
   int  j, k, m;
   int  x, y;

   k = j = m = 0;
   do {
      if (keyboard) k = keys;
      if (joypad) getjoypad(x, y, j);
      if (mouse) getmouse(x, y, m);
   } while (!k && !j && !m);
}


// Some device handling routines.
void (*getdevice(int n))(int &, int &, int &)
{
   return device[n].getmotion;
}

char *getdevicename(int n)
{
   return device[n].name;
}

// Scan all available devices starting with device n
// and return the next UNLOCKED device greater than n.
// Returning -1 if none could be found.
int nextdevice(int n)
{
   int   i;

   i = ndevices;
   do {
      if (++n >= ndevices) n = 0;
   } while (--i && device[n].locked);
   if (device[n].locked) return -1;
   return n;
}

// Lock device n.
// returning >0 if ok. 0 if lock attempt failed.
int lockdevice(int n)
{
   if (n >= ndevices) return 0;
   device[n].locked = 1;
   return n;
}

int unlockdevice(int n)
{
   if (n >= ndevices) return 0;
   device[n].locked = 0;
   return n;
}

// Dummy input device. The NONE device.
void getnone(int &x, int &y, int &fire)
{
   x = y = fire = 0;
}


/*---------------------------------------------------------
 Function: *

 Description:
 Initialize the INPUT SYSTEM.
 Prepare all available inputs and fill in device structures.
---------------------------------------------------------*/
void initinputs(int input)
{
   ndevices = 0;

// Dummy input device.
   device[ndevices].locked = 0;
   device[ndevices].name = "None";
   device[ndevices].getmotion = getnone;
   ndevices++;

   keyboard = joypad = mouse = 0;

// KEYBOARD.
   if (input & INPUT_KEYBOARD) {
      initkeyboard();
      if (keyboard) {
         device[ndevices].locked = 0;
         device[ndevices].name = "Keyboard";
         device[ndevices].getmotion = getkeyboard;
         ndevices++;
      }
   }

// JOYPAD.
   if (input & INPUT_JOYPAD) {
      initjoypad();
      if (joypad) {
         device[ndevices].locked = 0;
         device[ndevices].name = "Joypad";
         device[ndevices].getmotion = getjoypad;
         ndevices++;
      }
   }

// MOUSE.
   if (input & INPUT_MOUSE) {
      initmouse();
      if (mouse) {
         device[ndevices].locked = 0;
         device[ndevices].name = "Mouse";
         device[ndevices].getmotion = getmouse;
         ndevices++;
      }
   }
}

void shutinputs(void)
{
   shutmouse();
   shutjoypad();
   shutkeyboard();
}




